﻿using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Interop;

namespace UBCon.Ribbon
{
    using UBCon.Core.Enums;
    using UBCon.Ribbon.Interfaces;

    /// <summary>
    /// 
    /// </summary>
    [TemplatePart(Name = PART_DropDownButtonItemsHost, Type = typeof(Panel))]
    [TemplatePart(Name = PART_DropDownButtonPopupName, Type = typeof(Popup))]
    [TemplatePart(Name = PART_RibbonDropDownButtonThumbName, Type = typeof(Thumb))]
    [TemplatePart(Name = PART_RibbonDropDownButtonResizeName, Type = typeof(FrameworkElement))]
    [TemplatePart(Name = PART_ToggleName, Type = typeof(ToggleButton))]
    [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(DropDownMenuItem))]
    [Localizability(LocalizationCategory.Menu, Readability = Readability.Inherit)]
    public abstract class DropDownButtonBase : Menu, ICommandSource, IRibbonControl
    {
        //------------------------------------------------------
        //
        //  Constructors
        //
        //------------------------------------------------------
        #region Constructors

        /// <summary>
        /// Default static constructor.
        /// </summary>
        static DropDownButtonBase()
        {
            Type ownerType = typeof(DropDownButtonBase);

            KeyboardNavigation.DirectionalNavigationProperty.OverrideMetadata(ownerType, new System.Windows.FrameworkPropertyMetadata(KeyboardNavigationMode.Cycle));

        }

        /// <summary>
        /// Default ctor.
        /// </summary>
        public DropDownButtonBase()
            : base()
        {
            EventManager.RegisterClassHandler(typeof(DropDownButtonBase), 
                Mouse.PreviewMouseDownOutsideCapturedElementEvent, new MouseButtonEventHandler(OnOutsideElementCaptured));
        }


        private void OnOutsideElementCaptured(object sender, MouseButtonEventArgs e) 
        {
            if (e.ChangedButton == MouseButton.Left || e.ChangedButton == MouseButton.Right)
            {
                if (HasCapture)
                {
                    if (IsDropDownOpen)
                    {
                        CloseDropDown();
                    }                    
                }
            }
        }

        #endregion

        //------------------------------------------------------
        //
        //  Properties
        //
        //------------------------------------------------------
        #region Properties

        /// <summary>
        /// Gets a boolean value indicating whether the control has mouse capture or not.
        /// </summary>
        internal bool HasCapture
        {
            get
            {
                return Mouse.Captured == this;
                
            }
        }

        /// <summary>
        /// Gets or sets image source of the control.
        /// This is a dependency property.
        /// </summary>
        [Bindable(true),
        ReadOnly(false),
        Browsable(true),
        Category("Appearance")]
        public ImageSource Image
        {
            get { return (ImageSource)GetValue(ImageProperty); }
            set { SetValue(ImageProperty, value); }
        }

        /// <summary>
        /// Identifies UBCon.Ribbon.DropDownButtonBase.Image dependency property.
        /// </summary>
        public static readonly DependencyProperty ImageProperty = RibbonButton.ImageProperty.AddOwner(typeof(DropDownButtonBase));

        /// <summary>
        /// Gets or sets the text used for this control
        /// </summary>
        public string Title
        {
            get { return (string)GetValue(TitleProperty); }
            set { SetValue(TitleProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Title.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TitleProperty = RibbonButton.TitleProperty.AddOwner(typeof(DropDownButtonBase));

        [Bindable(true),
        Browsable(false),
        ReadOnly(false)]
        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Command.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.Register("Command", typeof(ICommand),
            typeof(DropDownButtonBase),
            new PropertyMetadata((ICommand)null));

        [Browsable(false),
        Bindable(true),
        ReadOnly(false)]
        public IInputElement CommandTarget
        {
            get { return (IInputElement)GetValue(CommandTargetProperty); }
            set { SetValue(CommandTargetProperty, value); }
        }

        // Using a DependencyProperty as the backing store for CommandTarget.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CommandTargetProperty =
            DependencyProperty.Register("CommandTarget", typeof(IInputElement),
            typeof(DropDownButtonBase),
            new PropertyMetadata((IInputElement)null));

        [Browsable(false),
        Bindable(true),
        ReadOnly(false)]
        public object CommandParameter
        {
            get { return (object)GetValue(CommandParameterProperty); }
            set { SetValue(CommandParameterProperty, value); }
        }

        // Using a DependencyProperty as the backing store for CommandParameter.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CommandParameterProperty =
            DependencyProperty.Register("CommandParameter", typeof(object),
            typeof(DropDownButtonBase),
            new PropertyMetadata((object)null));


        [Bindable(true),
        Category("Behavior")]
        public ClickMode ClickMode
        {
            get { return (ClickMode)GetValue(ClickModeProperty); }
            set { SetValue(ClickModeProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ClickMode.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ClickModeProperty =
            DependencyProperty.Register("ClickMode", typeof(ClickMode),
            typeof(DropDownButtonBase),
            new FrameworkPropertyMetadata(ClickMode.Release),
            new ValidateValueCallback(IsValidClickMode));

        [Bindable(true),
        ReadOnly(false)]
        public PlacementMode DropDownPlacement
        {
            get { return (PlacementMode)GetValue(DropDownPlacementProperty); }
            set { SetValue(DropDownPlacementProperty, value); }
        }

        // Using a DependencyProperty as the backing store for DropDownPlacement.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DropDownPlacementProperty =
            DependencyProperty.Register("DropDownPlacement", typeof(PlacementMode), typeof(DropDownButtonBase), new FrameworkPropertyMetadata(PlacementMode.Bottom));

        [Bindable(true),
        ReadOnly(false),
        Browsable(false)]
        public UIElement PlacementTarget
        {
            get { return (UIElement)GetValue(PlacementTargetProperty); }
            set { SetValue(PlacementTargetProperty, value); }
        }

        // Using a DependencyProperty as the backing store for PlacementTarget.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PlacementTargetProperty =
            DependencyProperty.Register("PlacementTarget", typeof(UIElement), typeof(DropDownButtonBase), new FrameworkPropertyMetadata((UIElement)null));

        [Bindable(true),
        ReadOnly(false),
        Browsable(true),
        Category("Appearance")]
        public RibbonControlSize Size
        {
            get { return (RibbonControlSize)GetValue(SizeProperty); }
            set { SetValue(SizeProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Size.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SizeProperty =
            DependencyProperty.Register("Size", typeof(RibbonControlSize), typeof(DropDownButtonBase), new FrameworkPropertyMetadata(RibbonControlSize.Small));

        [Bindable(true),
        ReadOnly(false),
        Browsable(false),
        Category("Behavior")]
        public bool IsDropDownOpen
        {
            get { return (bool)GetValue(IsDropDownOpenProperty); }
            set { SetValue(IsDropDownOpenProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsDropDownOpen.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsDropDownOpenProperty =
            DependencyProperty.Register("IsDropDownOpen", typeof(bool), typeof(DropDownButtonBase),
            new FrameworkPropertyMetadata
                (
                false,
                new PropertyChangedCallback(OnIsDropDownOpenPropertyChanged)));

        [Bindable(true),
        ReadOnly(false),
        Browsable(true),
        Category("Appearance")]
        public double MaxDropDownHeight
        {
            get { return (double)GetValue(MaxDropDownHeightProperty); }
            set { SetValue(MaxDropDownHeightProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MaxDropDownHeight.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MaxDropDownHeightProperty =
            DependencyProperty.Register("MaxDropDownHeight", typeof(double), typeof(DropDownButtonBase),
            new FrameworkPropertyMetadata(System.Windows.SystemParameters.PrimaryScreenHeight * 0.65));

        [Bindable(true),
        ReadOnly(false),
        Browsable(true), Category("Appearance")]
        public bool UserCanResizeDropDown
        {
            get { return (bool)GetValue(UserCanResizeDropDownProperty); }
            set { SetValue(UserCanResizeDropDownProperty, value); }
        }

        // Using a DependencyProperty as the backing store for UserCanResizeDropDown.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty UserCanResizeDropDownProperty =
            DependencyProperty.Register("UserCanResizeDropDown", typeof(bool), typeof(DropDownButtonBase), new FrameworkPropertyMetadata(false));


        public double MaxDropDownWidth
        {
            get { return (double)GetValue(MaxDropDownWidthProperty); }
            set { SetValue(MaxDropDownWidthProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MaxDropDownWidth.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MaxDropDownWidthProperty = DropDownMenuItem.MaxDropDownWidthProperty.AddOwner(typeof(DropDownButtonBase));

        public double DropDownHeight
        {
            get { return (double)GetValue(DropDownHeightProperty); }
            set { SetValue(DropDownHeightProperty, value); }
        }

        // Using a DependencyProperty as the backing store for DropDownHeight.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DropDownHeightProperty =
            DependencyProperty.Register("DropDownHeight", typeof(double), typeof(DropDownButtonBase), new PropertyMetadata(double.NaN));

        internal RibbonComboBox FilterComboBox
        {
            get { return (RibbonComboBox)GetValue(FilterComboBoxProperty); }
            set { SetValue(FilterComboBoxProperty, value); }
        }

        // Using a DependencyProperty as the backing store for FilterComboBox.  This enables animation, styling, binding, etc...
        internal static readonly DependencyProperty FilterComboBoxProperty =
            DependencyProperty.Register("FilterComboBox", typeof(RibbonComboBox), typeof(DropDownButtonBase), new PropertyMetadata(null));



        public double VerticalOffsetValue
        {
            get { return (double)GetValue(VerticalOffsetValueProperty); }
            set { SetValue(VerticalOffsetValueProperty, value); }
        }

        // Using a DependencyProperty as the backing store for VerticalOffsetValue.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty VerticalOffsetValueProperty =
            DependencyProperty.Register("VerticalOffsetValue", typeof(double), typeof(DropDownButtonBase),
            new PropertyMetadata(0.0D, new PropertyChangedCallback(OnVerticalOffsetValuePropertyChanged)));


        private static void OnVerticalOffsetValuePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            DropDownButtonBase drdown = (DropDownButtonBase)sender;
            if (drdown._dropDownPopup == null)
            {
                return;
            }

            double newValue = (double)e.NewValue;
            drdown._dropDownPopup.SetValue(Popup.VerticalOffsetProperty, newValue);
        }
        
        /// <summary>
        /// Gets drop down's content
        /// </summary>
        protected FrameworkElement DropDownContent
        {
            get 
            {
                return _dropDownPopup.Child as FrameworkElement; 
            }
        }

        /// <summary>
        /// Gets root visual child of drop-down
        /// </summary>
        private Visual RootPopup 
        {
            get 
            {
                if (_rootPopup == null) 
                {
                    HwndSource source = PresentationSource.FromVisual(_dropDownPopup) as HwndSource;

                    if (source != null && source.RootVisual != null)
                    {
                        _rootPopup = source.RootVisual as FrameworkElement;
                    }
                }
                return _rootPopup;
            }
        }

        #endregion

        //------------------------------------------------------
        //
        //  Depenency Property Changed Callbacks
        //
        //------------------------------------------------------
        #region Depenency Property Changed Callbacks

        private static void OnIsDropDownOpenPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            DropDownButtonBase drDown = (DropDownButtonBase)sender;
            bool newValue = (bool)e.NewValue;
            bool oldValue = !newValue;
            if (newValue)
            {
                if (drDown.IsResizable)
                {
                    drDown.resizeGrip.SetValue(FrameworkElement.HeightProperty, double.NaN);
                }
                drDown.TryAnimateDropDownContent();

                drDown.OnDropDownOpened();
            }
            else
            {
                //If any control got mouse  capture we 
                //force to release capture
                if (Mouse.Captured != null)
                {
                    Mouse.Capture(null);
                    drDown.ReleaseMouseCapture();
                }
                drDown.RestoreContent();
                drDown.OnDropDownClosed();
            }
        }

        private static void OnMaxDropDownHeightPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {

        }

        private static bool IsValidClickMode(object value)
        {
            ClickMode clickMode = (ClickMode)value;
            return clickMode == ClickMode.Press
                || clickMode == ClickMode.Release
                || clickMode == ClickMode.Hover;
        }

        #endregion

        //------------------------------------------------------
        //
        //  Overrides
        //
        //------------------------------------------------------
        #region Overrides

        /// <summary>
        ///Creates or identifies the element that is used to display the given item.
        /// </summary>
        /// <returns>Type: System.Windows.DependencyObject. The element that is used to display the given item.</returns>
        protected override DependencyObject GetContainerForItemOverride()
        {
            return new DropDownMenuItem() 
            {
                ParentMenu = this 
            };
        }

        public virtual void OnDropDownOpened()
        {
            RoutedEventArgs args = new RoutedEventArgs(DropDownOpenedEvent);
            RaiseEvent(args);
        }

        public virtual void OnDropDownClosed()
        {
            RoutedEventArgs args = new RoutedEventArgs(DropDownClosedEvent);
            RaiseEvent(args);
        }

        /// <summary>
        /// Determines if the specified item is (or is eligible to be) its own ItemContainer. 
        /// (Overrides ItemsControl.IsItemItsOwnContainerOverride(Object).)
        /// </summary>
        /// <param name="item">Specified item.</param>
        /// <returns>true if the item is its own ItemContainer; otherwise, false.</returns>
        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            bool isValidItem = item is DropDownMenuItem || item is RibbonSeparator || item is RibbonGallerySet;

            if (isValidItem)
            {
                if (item is DropDownMenuItem)
                {
                    DropDownMenuItem menuItem = item as DropDownMenuItem;
                    menuItem.ParentMenu = this;
                }
                else if (item is RibbonGallerySet) 
                {
                    RibbonGallerySet rSet = item as RibbonGallerySet;
                    
                }
            }
            return isValidItem;
        }

        /// <summary>
        /// Invoked whenever application code or internal processes (such as a rebuilding layout pass) call ApplyTemplate. 
        /// In simplest terms, this means the method is called just before a UI element displays in  app.  
        /// </summary>
        public override void OnApplyTemplate()
        {

            if (_thumb != null)
            {
                _thumb.DragDelta -= _thumb_DragDelta;
            }

            ItemsHost = GetTemplateChild(PART_DropDownButtonItemsHost) as Panel;

            if (ItemsHost == null && ItemsPanel != null)
            {
                ItemsHost = ItemsPanel.LoadContent() as Panel;
            }

            this._dropDownPopup = GetTemplateChild(PART_DropDownButtonPopupName) as Popup;

            if (ContentCanBeAnimated)
            {
                RestoreContent();
            }

            _thumb = GetTemplateChild(PART_RibbonDropDownButtonThumbName) as Thumb;

            if (_thumb != null)
            {
                _thumb.DragDelta += _thumb_DragDelta;
            }

            resizeGrip = GetTemplateChild(PART_RibbonDropDownButtonResizeName) as FrameworkElement;

            toggle = GetTemplateChild(PART_ToggleName) as ToggleButton;

            base.OnApplyTemplate();
        }

        /// <summary>
        /// Occurs when an element loses mouse capture.
        /// </summary>
        /// <param name="e">Event data for the event.</param>
        protected override void OnLostMouseCapture(MouseEventArgs e)
        {
            //FrameworkElement captured = Mouse.Captured as FrameworkElement;
            //if (captured != null)
            //{
            //    FrameworkElement parent = UBCon.Core.CommonUtilities.GetVisualRoot(captured) as FrameworkElement;

            //    if (parent != null && parent.Parent == _dropDownPopup)
            //    {
            //        e.Handled = true;

            //        if (captured is RibbonGalleryItem)
            //        {
            //            ItemsControl itemsControl = ItemsControl.ItemsControlFromItemContainer(captured);

            //            if (itemsControl != null)
            //            {
            //                bool staysOpen = (bool)itemsControl.GetValue(RibbonGallery.StaysOpenOnSelectionProperty);

            //                if (!staysOpen)
            //                {
            //                   // CloseDropDown();
            //                }
            //            }
            //        }
                   
            //    }
            //}
          
            base.OnLostMouseCapture(e);
        }

        #endregion

        //------------------------------------------------------
        //
        //  Public Methods
        //
        //------------------------------------------------------
        #region Public Methods
        #endregion

        //------------------------------------------------------
        //
        //  Internal Methods
        //
        //------------------------------------------------------
        #region Internal Methods
        #endregion

        //------------------------------------------------------
        //
        //  Private Methods
        //
        //------------------------------------------------------
        #region Private Methods

        private void BuildShowContentAnimation()
        {

            DoubleAnimation da = new DoubleAnimation()
            {
                EasingFunction = new ExponentialEase()
                {
                    EasingMode = EasingMode.EaseOut,
                    Exponent = 5
                }
            };
            TranslateTransform transform = _dropDownPopup.Child.RenderTransform as TranslateTransform;
            //_dropDownPopup.
            Storyboard.SetTargetProperty(da, new PropertyPath(propertyPath));
            Storyboard.SetTarget(da, DropDownContent);
            ShowContentStoryboard = new Storyboard();
            da.From = -5D;
            da.To = 0.0D;
            da.Duration = new Duration(TimeSpan.FromSeconds(0.20));
            ShowContentStoryboard.Children.Add(da);
        }

        private void BuildHideContentStoryboard()
        {
            HideContentStoryboard = new Storyboard();
            DoubleAnimation da = new DoubleAnimation();
            Storyboard.SetTargetProperty(da, new PropertyPath(propertyPath));
            Storyboard.SetTarget(da, DropDownContent);
            da.From = 0.0D;
            da.To = -5;
            da.Duration = new Duration(TimeSpan.FromSeconds(0.02));
            HideContentStoryboard.Children.Add(da);
        }
        #endregion

        //------------------------------------------------------
        //
        //  Event Handlers
        //
        //------------------------------------------------------
        #region Event Handlers

        private void _thumb_DragDelta(object sender, DragDeltaEventArgs e)
        {
            double height = resizeGrip.ActualHeight;

            height += e.VerticalChange;

            if (height >= resizeGrip.MinHeight + 2 && height <= MaxDropDownHeight)
            {
                resizeGrip.SetValue(FrameworkElement.HeightProperty, height);
            }
        }

        #endregion

        //------------------------------------------------------
        //
        //  Fields
        //
        //------------------------------------------------------
        #region Fields

        //Template part names
        private const string PART_DropDownButtonItemsHost = "PART_DropDownButtonItemsHost",
                             PART_DropDownButtonPopupName = "PART_DropDownButtonPopup",
                             PART_RibbonDropDownButtonThumbName = "PART_RibbonDropDownButtonThumb",
                             PART_RibbonDropDownButtonResizeName = "PART_RibbonDropDownButtonResize",
                             PART_ToggleName = "PART_Toggle",
                             PopupRootName = "System.Windows.Controls.Primitives.PopupRoot";
        //property path to animate
        private const string propertyPath = "(UIElement.RenderTransform).(TranslateTransform.Y)";

        //content animation
        private Storyboard ShowContentStoryboard, HideContentStoryboard;

        #endregion

        //------------------------------------------------------
        //
        //  Events
        //
        //------------------------------------------------------
        #region Events

        public event RoutedEventHandler DropDownOpened
        {
            add { AddHandler(DropDownOpenedEvent, value); }
            remove { RemoveHandler(DropDownOpenedEvent, value); }
        }

        public static readonly RoutedEvent DropDownOpenedEvent = EventManager.RegisterRoutedEvent
           (
               "DropDownOpened",
                RoutingStrategy.Bubble,
                typeof(RoutedEventHandler),
                typeof(DropDownButtonBase)
           );

        public event RoutedEventHandler DropDownClosed
        {
            add { AddHandler(DropDownClosedEvent, value); }
            remove { RemoveHandler(DropDownClosedEvent, value); }
        }

        public static readonly RoutedEvent DropDownClosedEvent = EventManager.RegisterRoutedEvent
            (
                "DropDownClosed",
                 RoutingStrategy.Bubble,
                 typeof(RoutedEventHandler),
                 typeof(DropDownButtonBase)
            );
        #endregion

        //------------------------------------------------------
        //
        //  Internal protected members
        //
        //------------------------------------------------------
        #region Internal protected members

        //items panel
        protected internal Panel ItemsHost;

        protected internal Popup _dropDownPopup;

        protected internal Thumb _thumb;

        protected internal FrameworkElement resizeGrip;

        protected internal Visual _rootPopup;

        protected internal ToggleButton toggle;

        protected internal void SetFocusAtFirstElement()
        {
            if (IsItemsHostVisible)
            {
                //  this.ItemsHost.Focus();
                if (ItemsHost.Children.Count > 0)
                {
                    IInputElement elt = ItemsHost.Children[0];
                    if (elt != null)
                    {
                        Keyboard.PrimaryDevice.Focus(elt);
                    }
                }
            }
        }

        protected internal void RestoreContent()
        {
            if (HideContentStoryboard == null)
            {
                BuildHideContentStoryboard();
            }
            HideContentStoryboard.Begin();
        }

        protected internal void TryAnimateDropDownContent()
        {
            if (ContentCanBeAnimated)
            {
                if (ShowContentStoryboard == null)
                {
                    BuildShowContentAnimation();
                }
                ShowContentStoryboard.Begin();
            }
        }

        /// <summary>
        /// Indicates whether items host panel is avaliable or not
        /// </summary>
        protected internal bool IsItemsHostVisible
        {
            get
            {
                return ItemsHost != null;
            }
        }

        /// <summary>
        /// Indicates whether drop down content can be animated or not
        /// </summary>
        protected internal bool ContentCanBeAnimated
        {
            get
            {
                return _dropDownPopup != null
                        && _dropDownPopup.Child != null
                            && _dropDownPopup.Child is FrameworkElement
                            && _dropDownPopup.Child.RenderTransform is TranslateTransform;
            }
        }

        protected internal bool IsResizable
        {
            get
            {
                return _dropDownPopup != null &&
                        _dropDownPopup.Child != null &&
                            _dropDownPopup.Child is FrameworkElement
                                && UserCanResizeDropDown;
            }
        }

        #endregion

        //------------------------------------------------------
        //
        //  Internal virtual members
        //
        //------------------------------------------------------
        #region Internal virtual members

        internal virtual void CloseDropDown()
        {
            if (IsDropDownOpen)
            {
                IsDropDownOpen = false;
            }
        }

        #endregion

        //------------------------------------------------------
        //
        //  IRibbonControl Implementation
        //
        //------------------------------------------------------
        #region IRibbonControl Implementation

        /// <summary>
        /// Gets a boolean value indicating whether the contol's size is large
        /// </summary>
        public bool IsLarge
        {
            get { return this.Size == RibbonControlSize.Large; }
        }

        #endregion
    }
}
